Technical Q&A PS06
Yielding Time Without Getting Events


Q: I'm writing a library to be used by many applications and I need to yield time to other processes while waiting for a lengthy network operation. If I call WaitNextEvent with an eventMask of 0, I still get suspend and resume events. How do I defer these events?

A: You cannot defer suspend and resume events. You must handle them as the Process Manager gives them to you, or not at all. As a library developer, you have the following options for yielding time.

Callback

The best approach is for your library to make a callback to the host application whenever it wants to yield time. The host application can then yield using a mechanism that's appropriate for its environment. For example, it might call WaitNextEvent and handle the resulting events properly. Alternatively, if the host application is using the Thread Manager, it might yield time to other threads within the application by calling YieldToAnyThread from within the callback.

This approach has the advantage that it results in the simplest code for you, the library developer. The primary disadvantage is that it's possible for the host application to call your library again from within the yield callback. If your library is not prepared for this sort of reentrancy you must guard against it (or at least explicitly outlaw it in your documentation).

Modal Dialogs

If a callback is not possible, the only other correct solution is for your library to display a modal dialog while it is yielding time. A movable modal dialog is not sufficient -- the dialog must be system modal! The presence of the modal dialog will prevent the Process Manager from switching the host application to the background, and hence prevent the generation of suspend events.

This approach has an obvious problem: it results in a bad user experience. It also has a more subtle problem: an unexpected modal dialog in the application's layer can cause pending update perils.

Ostrich Algorithm

An incorrect but expedient approach is to simply call WaitNextEvent with an eventMask of 0 and throw away the suspend and resume events. This will cause some problems for your host application (for example, it won't convert between private and public scraps, and its windows may not activate and deactivate properly) but those problems are mostly cosmetic and unlikely to be system fatal.


WARNING:
Calling EventAvail rather than WaitNextEvent might seem like a solution to this problem. After all, Inside Macintosh: Processes says that EventAvail will yield time to other applications without actually returning any events. Unfortunately, this does not work. In fact, if you call EventAvail with an eventMask of 0 which there is a suspend event pending, your application will get stuck in a half suspended state; the Process Manager cannot schedule other application because it is continuously trying to get your application to accept the suspend event.



Note:
Programmers who remember the Windows 3.1 environment should note that EventAvail is not equivalent to Windows' OSYield routine. There is no direct analog to OSYield on traditional Mac OS because there is no way to yield time to other processes without also receiving user interface events.


To summarize, the best way of yielding time from a library when you're unsure of the host application's environment is to call back to the host application and let it do the yielding.

[Oct 16 2000]


Developer Documentation | Technical Notes | Development Kits | Sample Code